package com.qby.project.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.qby.common.enums.MessageType;
import com.qby.common.enums.RedisMessageType;
import com.qby.common.exceptions.ImageCodeException;
import com.qby.common.exceptions.MessageCodeException;
import com.qby.common.utils.AssertUtils;
import com.qby.framework.constant.RedisConstant;
import com.qby.framework.redis.RedisCache;
import com.qby.framework.web.AjaxResult;
import com.qby.project.service.IAppCommonService;
import com.qby.project.vo.FileVo;
import com.qby.project.vo.MessageVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 公共类实现
 * @author: qiu bo yang
 * @create: 2020-03-27 10:03
 */
@Slf4j
@Service
public class AppCommonServiceImpl implements IAppCommonService {
    //验证码有效时间
    @Value("${captcha.expire.time}")
    private Integer captchaExpireTime;
    //上传路径
    @Value("${file.resource.location}")
    private String resourceLocation;
    //服务器地址
    @Value("${service.address.url}")
    private String serviceAddressUrl;
    //文件名称长度
    @Value("${file.name.length}")
    private String fileNameLengths;
    //文件大小
    @Value("${file.size}")
    private String fileSizes;
    //类型
    @Value("${spring.profiles.active}")
    private String active;
    //短信url
    @Value("${sms.chuangRui.url}")
    private String chuangRuiUrl;
    //key
    @Value("${sms.chuangRui.accesskey}")
    private String chuangRuiAccesskey;
    //secret
    @Value("${sms.chuangRui.secret}")
    private String chuangRuiSecret;
    //签名sign
    @Value("${sms.chuangRui.sign}")
    private String chuangRuiSign;
    //短信模板
    @Value("${sms.chuangRui.templateId}")
    private String chuangRuiTemplateId;
    //redis服务层
    @Autowired
    private RedisCache redisCache;

    /**
     * 图形验证码
     *
     * @param random   随机字符
     * @param response
     */
    @Override
    public void getImageCaptcha(String random, HttpServletResponse response) {
        AssertUtils.notNull(random, "random随机数不能为空");
        if (redisCache.exist(RedisConstant.CAPTCHA + random)) {
            throw new ImageCodeException("图片验证码已存在,请稍后获取！");
        }
        try {
            // 定义图形验证码的长、宽、验证码字符数、干扰线宽度
            CircleCaptcha captchaCircle = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
            captchaCircle.write(response.getOutputStream());
            log.info("图文验证码：{}", captchaCircle.getCode());
            redisCache.setCacheObjectSeconds(RedisConstant.CAPTCHA + random, captchaCircle.getCode(), captchaExpireTime);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 上传单个文件
     *
     * @param file
     * @return
     */
    @Override
    public AjaxResult uploadFile(MultipartFile file) {
        FileVo fileVo = new FileVo();
        try {
            //获取文件输入流
            InputStream in = file.getInputStream();
            //文件名称
            String fileName = file.getOriginalFilename();
            //判断文件名称
            int fileNameLength = fileName.length();
            if (fileNameLength > Integer.valueOf(fileNameLengths)) {
                throw new MessageCodeException("上传文件名称过长");
            }
            //获取文件长度
            long size = file.getSize();
            if (size > Long.valueOf(fileSizes)) {
                throw new MessageCodeException("上传文件过大");
            }
            //路径创建文件夹
            File Folder = new File(resourceLocation);
            if (!Folder.exists()) {
                Folder.mkdirs();
            }
            //获取文件后缀名-类型
            String suffixType = fileName.substring(fileName.lastIndexOf("."));
            //文件上传路径
            String filePath = resourceLocation + System.currentTimeMillis() + suffixType;
            OutputStream os = new FileOutputStream(filePath);
            byte[] b = new byte[1024];
            int num;
            while ((num = in.read(b)) != -1) {
                os.write(b, 0, num);
            }
            in.close();
            os.close();
            //设置并返回全路径
            fileVo.setFileUrl(serviceAddressUrl + filePath);
            fileVo.setFileName(fileName);
            fileVo.setFileSize(size);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return AjaxResult.success(fileVo);
    }

    /**
     * 上传多个文件
     *
     * @param files
     * @return
     */
    @Override
    public AjaxResult uploadFiles(MultipartFile[] files) {
        FileVo fileVo = new FileVo();
        if (files != null && files.length > 1) {
            StringBuffer sb = new StringBuffer();
            for (MultipartFile file : files) {
                try {
                    //获取文件输入流
                    InputStream in = file.getInputStream();
                    //文件名称
                    String fileName = file.getOriginalFilename();
                    //判断文件名称
                    int fileNameLength = fileName.length();
                    if (fileNameLength > Integer.valueOf(fileNameLengths)) {
                        throw new RuntimeException("上传文件名称过长");
                    }
                    //获取文件长度
                    long size = file.getSize();
                    if (size > Long.valueOf(fileSizes)) {
                        throw new RuntimeException("上传文件过大");
                    }
                    //路径创建文件夹
                    File Folder = new File(resourceLocation);
                    if (!Folder.exists()) {
                        Folder.mkdirs();
                    }
                    //获取文件后缀名-类型
                    String suffixType = fileName.substring(fileName.lastIndexOf("."));
                    //文件上传路径
                    String filePath = resourceLocation + System.currentTimeMillis() + suffixType;
                    OutputStream os = new FileOutputStream(filePath);
                    byte[] b = new byte[1024];
                    int num;
                    while ((num = in.read(b)) != -1) {
                        os.write(b, 0, num);
                    }
                    in.close();
                    os.close();
                    //设置并返回全路径
                    sb.append(serviceAddressUrl + filePath + ",");
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            fileVo.setFileUrl(sb.toString().substring(0, sb.toString().length() - 1));
        }
        return AjaxResult.success(fileVo);
    }

    /**
     * 发送短信
     *
     * @param messageVo 短信参数
     * @return
     */
    @Override
    public AjaxResult sendMessage(MessageVo messageVo) {
        AssertUtils.notNull(messageVo.getRandom(), "图文验证码随机字符");
        AssertUtils.notNull(messageVo.getPhone(), "手机号必填");
        AssertUtils.notNull(messageVo.getImageCode(), "图形验证码必填");
        AssertUtils.notNull(messageVo.getMessageType(), "短信类型必填");

        if (!redisCache.exist(RedisConstant.CAPTCHA + messageVo.getRandom())) {
            return AjaxResult.error("图形验证码已过期");
        }

        String trueCode = redisCache.getCacheObject(RedisConstant.CAPTCHA + messageVo.getRandom());
        if (!trueCode.equalsIgnoreCase(messageVo.getImageCode())) {
            return AjaxResult.error("图形验证码不正确");
        }

        // 发送注册短信验证码
        if (MessageType.REGISTER.getCode().equals(messageVo.getMessageType())) {
            sendPhoneMessage(messageVo.getPhone(), RedisMessageType.REGISTER);
            //发送忘记密码短信验证码
        } else if (MessageType.FORGET_PASSWORD.getCode().equals(messageVo.getMessageType())) {
            sendPhoneMessage(messageVo.getPhone(), RedisMessageType.FORGET_PASSWORD);
            //发送绑定手机号码短信验证码
        } else if (MessageType.BINDING_PHONE.getCode().equals(messageVo.getMessageType())) {
            sendPhoneMessage(messageVo.getPhone(), RedisMessageType.BINDING_PHONE);
        } else {
            return AjaxResult.error("短信类型有误");
        }
        return AjaxResult.success("短信发送成功");
    }

    /**
     * 发送短信方法
     *
     * @param phone            手机号码
     * @param redisMessageType redis短信类型
     */
    private synchronized void sendPhoneMessage(String phone, RedisMessageType redisMessageType) {
        String key = redisMessageType.getRedisKey() + phone;
        if (redisCache.getCacheObject(key) != null) {
            throw new MessageCodeException("该手机号已发送过短信验证码");
        }
        String code = "123456";
        if ("pro".equals(active)) {
            code = RandomUtil.randomNumbers(6);
            // 发送短信平台
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("accesskey", chuangRuiAccesskey);
            paramMap.put("secret", chuangRuiSecret);
            paramMap.put("sign", chuangRuiSign);
            paramMap.put("templateId", chuangRuiTemplateId);
            paramMap.put("mobile", phone);
            paramMap.put("content", code);
            String result = HttpUtil.post(chuangRuiUrl, paramMap);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            if ("0".equals(jsonObject.getStr("code"))) {
                log.info("创瑞短信返回结果：{}", result);
            } else {
                throw new MessageCodeException("短信验证码发送失败");
            }
        }
        log.info("-----短信验证码：{}-----", code);
        log.info("-----手机号码：{}-----", phone);

        // 保存到redis
        redisCache.setCacheObjectSeconds(key, code, 600);
    }
}
